package com.modular.handler;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.modular.constant.BaseCodeDesc;
import com.modular.exception.CodeDesc;
import com.modular.exception.ModularException;
import com.modular.result.CodeDescUtils;
import com.modular.result.RspMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;

import java.lang.reflect.Method;
import java.net.SocketTimeoutException;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

@RestControllerAdvice
public class GlobalExceptionHandler {
    private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    @ExceptionHandler({Exception.class})
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public RspMessage<Object> exception(Exception exception) {
        logger.error("Exception 捕获到未被处理的异常: {}", exception.getMessage(), exception);
        return coverMessage(exception);
    }

    @ExceptionHandler({ModularException.class})
    @ResponseStatus(HttpStatus.OK)
    public RspMessage<Object> baseException(ModularException ex) {
        String code = ex.getCode();
        CodeDesc busCodeDesc = new CodeDesc(ex.getCode(),ex.getErrMsg());
        Object[] params = ex.getParams();
        if (ArrayUtil.isNotEmpty(params)) {
            String template = busCodeDesc.getErrMsg();
            if (template.contains("{}")) {
                busCodeDesc.setErrMsg(StrUtil.format(template, params));
            } else {
                busCodeDesc.setErrMsg(String.format(template, params));
            }
        }

        logger.error("BaseUncheckedException 自定义业务异常类[{}], {}", new Object[]{ex.getClass().getName(), busCodeDesc.toString(), ex});
        return RspMessage.result(busCodeDesc);
    }

//    @ExceptionHandler({SecurityBizException.class})
//    @ResponseStatus(HttpStatus.FORBIDDEN)
//    public RspMessage<Object> securityException(SecurityBizException ex) {
//        String bizCodeDescKey = ex.getBizCodeDescKey();
//        String message = ex.getMessage();
//        CodeDesc busCodeDesc = this.codeDescUtils.getCodeDesc(bizCodeDescKey);
//        if (StrUtil.isNotEmpty(message)) {
//            busCodeDesc.setRemark(message);
//        }
//
//        log.error("SecurityBizException 自定义权限异常[{}], {}", ex.getClass().getName(), busCodeDesc.toString());
//        return RspMessage.result(busCodeDesc);
//    }

    @ConditionalOnClass({AccessDeniedException.class})
    @ExceptionHandler({AccessDeniedException.class})
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public RspMessage<Object> accessDeniedException(AccessDeniedException ex) {
//        CodeDesc busCodeDesc = this.codeDescUtils.getCodeDesc("sys.codedesc.forbidden");
        logger.warn("AccessDeniedException 访问资源权限不足: {}", ex.getMessage(), ex);
        return coverMessage(ex);
    }

    @ExceptionHandler({NullPointerException.class})
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public RspMessage<Object> nullPointerException(NullPointerException ex) {
//        CodeDesc codeDesc = this.codeDescUtils.getCodeDesc("sys.codedesc.fail");
        logger.error("NullPointerException 空指针异常: {}", ex.getMessage(), ex);
        return coverMessage(ex);
    }

    @ExceptionHandler({HttpRequestMethodNotSupportedException.class})
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public RspMessage<Object> methodNotSupportedException(HttpRequestMethodNotSupportedException ex) {
        String supportMethod = (String)Optional.ofNullable(ex.getSupportedMethods()).map((strings) -> {
            return strings[0];
        }).orElse("");
        logger.error("HttpRequestMethodNotSupportedException 接口使用了错误的请求方式: [{}], {}", supportMethod, ex.getMessage());
//        CodeDesc codeDesc = this.codeDescUtils.getCodeDesc("sys.codedesc.badrequest");
//        String var10000 = codeDesc.getMessage();
//        String var10001 = codeDesc.getRemark();
        return coverMessage(ex);
    }

    @ExceptionHandler({HttpMessageNotReadableException.class})
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public RspMessage<Object> notReadableException(HttpMessageNotReadableException ex) {
        logger.error("HttpMessageNotReadableException 数据转换异常: {}", ex.getMessage());
//        CodeDesc codeDesc = this.codeDescUtils.getCodeDesc("sys.codedesc.paramerror");
//        return RspMessage.result(codeDesc, ex.getMessage());
        return coverMessage(ex);
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler({MissingServletRequestParameterException.class})
    public RspMessage<Object> missingServletRequestParameterException(MissingServletRequestParameterException ex) {
        logger.warn("MissingServletRequestParameterException 请求接口缺失必要参数异常: {}", ex.getMessage());
//        CodeDesc codeDesc = this.codeDescUtils.getCodeDesc("sys.codedesc.paramerror");
//        String var10000 = codeDesc.getMessage();
//        String var10001 = codeDesc.getRemark();
//        return RspMessage.result(var10000, var10001 + ":" + ex.getMessage());
        return coverMessage(ex);
    }

    @ExceptionHandler({BindException.class})
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public RspMessage<Object> bindException(BindException ex) {
        String message = (String)ex.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(","));
        logger.warn("BindException 参数校验失败: {}", message);
        return coverMessage(ex);
    }

    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler({MethodArgumentNotValidException.class})
    public RspMessage<Object> validExceptionHandler(MethodArgumentNotValidException ex) {
        String message = (String)ex.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(","));
        String methodName = ((Method) Objects.requireNonNull(ex.getParameter().getMethod())).getName();
        logger.warn("MethodArgumentNotValidException: [{}]方法数据校验失败: {}", methodName, message);
        return coverMessage(ex);
    }

//    @ExceptionHandler({ConstraintViolationException.class})
//    @ResponseStatus(HttpStatus.OK)
//    public RspMessage<Object> handleConstraintViolationException(ConstraintViolationException ex) {
//        String message = (String)ex.getConstraintViolations().stream().map(ConstraintViolation::getMessage).collect(Collectors.joining(","));
//        log.warn("ConstraintViolationException: 数据校验失败: {}", message);
//        return this.resultParamError(message);
//    }
//
//    private RspMessage<Object> resultParamError(String message) {
//        CodeDesc codeDesc = this.codeDescUtils.getCodeDesc("sys.codedesc.paramerror");
//        String var10000 = codeDesc.getMessage();
//        String var10001 = codeDesc.getRemark();
//        return RspMessage.result(var10000, var10001 + ":" + message);
//    }

    @ResponseStatus(HttpStatus.PAYLOAD_TOO_LARGE)
    @ExceptionHandler({MaxUploadSizeExceededException.class})
    public RspMessage<Object> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e) {
        logger.error("SizeLimitExceededException 上传文件大小超过了限定: {}, 最大上传大小: {}", new Object[]{e.getMessage(), e.getMaxUploadSize(), e});
        return coverMessage(e);
    }

    @ResponseStatus(HttpStatus.REQUEST_TIMEOUT)
    @ExceptionHandler({SocketTimeoutException.class})
    public RspMessage<Object> socketTimeoutException(SocketTimeoutException ex) {
        logger.error("SocketTimeoutException 请求超时: {}", ex.getMessage(), ex);
//        CodeDesc codeDesc = this.codeDescUtils.getCodeDesc("sys.codedesc.exception");
        return coverMessage(ex);
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler({MethodArgumentTypeMismatchException.class})
    public RspMessage<Object> methodArgumentTypeMismatchException(MethodArgumentTypeMismatchException ex) {
        logger.error("MethodArgumentTypeMismatchException {}() 参数[{}]不能设置为[{}]", new Object[]{((Class)Objects.requireNonNull(ex.getRequiredType())).getName(), ex.getName(), ex.getValue()});
//        CodeDesc codeDesc = this.codeDescUtils.getCodeDesc("sys.codedesc.paramerror");
        return coverMessage(ex);
    }

    @ExceptionHandler({HttpMediaTypeNotSupportedException.class})
    @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
    public RspMessage<Object> httpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException ex) {
        logger.warn("HttpMediaTypeNotSupportedException 请求类型(Content-Type)与实际接口的请求类型不匹配: {}", ex.getMessage());
//        CodeDesc codeDesc = this.codeDescUtils.getCodeDesc("sys.codedesc.methodnotallowed");
//        String var10000 = codeDesc.getMessage();
//        String var10001 = codeDesc.getRemark();
        return coverMessage(ex);
    }

    @ExceptionHandler({MissingServletRequestPartException.class})
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public RspMessage<Object> missingServletRequestPartException(MissingServletRequestPartException ex) {
        logger.warn("MissingServletRequestPartException 请求中缺少有效的文件: {}", ex.getRequestPartName(), ex);

        return coverMessage(ex);
    }

    private RspMessage<Object> coverMessage(Exception e){
        String errMsg = StrUtil.EMPTY;
        if(e instanceof ModularException){
            ModularException me = (ModularException)e;
            return RspMessage.result(me.getCode(),me.getMessage(),null);
        }else if(e instanceof MethodArgumentNotValidException){
            errMsg = (String)((MethodArgumentNotValidException)e).getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(","));
        }else {
            errMsg = e.getMessage();
        }

        CodeDesc codeDesc = new CodeDesc();
        codeDesc.setCode(BaseCodeDesc.DEFATUL_MSG.getCode());
        codeDesc.setErrMsg(StrUtil.format(BaseCodeDesc.DEFATUL_MSG.getErrMsg(),errMsg));
        return RspMessage.result(codeDesc);
    }
}
